<?php
/**
 *	KLF Framework
 *
 *	Written by Kristian Oye <kristianoye@gmail.com> on Dec 17, 2011
 *	Copyright (C) 2010-2011
 */
namespace KLF\System\Errors
{
	/**
	 *  Class KExceptionHandler
	 *
	 *  Extensible / configurable error handler.
	 *
	 *  @package KLF\System\Errors
	 *  @access public
	 *  @author Kristian Oye <kristianoye@gmail.com>
	 *  @since 0.1
	 *
	 *  @method KExceptionHandler NewInstance
	 */
	class KExceptionHandler
	{
		protected $_rpropDefaultHandler;
		
		protected $_rpropExceptionHandlers;
		
		/**
		 * Construct a new error handler object.
		 */
		public function __construct()
		{
			$this->_rpropExceptionHandlers;
		}
		
		/**
		 * Adds a handler for a type of exception.
		 * 
		 * @param KException $ex
		 * @param unknown_type $callback
		 */
		public function AddHandler(\Exception $ex, $callback, $rethrow = false)
		{
			if (!is_callable($callback))
			{
				throw new KArgumentException("Parameter 2 to AddHandler(exception, callback) must be callable.");
			}
			elseif (isset($this->_rpropExceptionHandlers[get_class($ex)]))
			{
				throw new KArgumentException("There is already a handler defined for ".get_class($ex));
			}
			else
			{
				$this->_rpropExceptionHandlers[get_class($ex)] = array(
					'callback'	=> $callback,
					'rethrow'	=> $rethrow);
			}
		}
		
		/**
		 * Sets the default handler should an exact matching not exist.
		 * 
		 * @param unknown_type $callback
		 * @param unknown_type $rethrow
		 */
		public function DefaultHandler($callback, $rethrow = false)
		{
			$this->_rpropDefaultHandler = array(
				'callback'	=> $callback,
				'rethrow'	=> $rethrow);
		}
		
		/**
		 * Perform callback and rethrow error if required.
		 * 
		 * @param \Exception $ex
		 * @param array $data
		 * @throws Exception
		 */
		private function HandleExceptionInner(\Exception $ex, $data)
		{
			$handler = $data['callback'];
			$rethrow = $data['rethrow'];
			
			//  Exception was handled
			if ($handler($ex))
			{
				return true;
			}
			//  Error should be rethrown
			elseif ($rethrow)
			{
				throw $ex;
			}
			return false;
		}
		
		/**
		 * Handle an instance of an actual exception.
		 * 
		 * @param KException $ex
		 */
		public function HandleException(\Exception $ex)
		{
			//  Check for exact match
			if (isset($this->_rpropExceptionHandlers[get_class($ex)]))
			{
				return $this->HandleExceptionInner($ex, $this->_rpropExceptionHandlers[get_class($ex)]);
			}
			else
			{
				foreach($this->_rpropExceptionHandlers as $classType => $data)
				{
					if (is_a($ex, $classType))
					{
						return $this->HandleExceptionInner($ex, $data);
					}
				}
				if (isset($this->_rpropDefaultHandler))
				{
					return $this->HandleExceptionInner($ex, $this->_rpropDefaultHandler);
				}
			}
		}
	}
}
